"""
@Author：十
@Time：2025/5/13 9:32
@FileName：device.py
@Description：设备小窗口的逻辑
"""
import os
import re
import sqlite3
import sys
from configparser import ConfigParser

from PyQt5 import QtCore
from PyQt5.QtCore import Qt, QPoint, pyqtSignal
from PyQt5.QtGui import QMouseEvent
from PyQt5.QtSerialPort import QSerialPortInfo
from PyQt5.QtWidgets import QWidget, QApplication, QTableWidgetItem, QMessageBox
from loguru import logger

from UI.settings import Ui_widget
from addDevice import AddWindow
from showDevice import ShowDevice
import resource.WTC_rc


class SettingWindow(QWidget, Ui_widget):
    Signal_Refresh_Data = pyqtSignal()
    finished = pyqtSignal()

    def __init__(self):
        super(SettingWindow, self).__init__()
        self._tracking = False
        self._startPos = None
        self._endPos = None
        self.Serial_QThread_Function = None
        self.setupUi(self)

        self.pushButton_config.clicked.connect(self.config_sittings)

        self.pushButton_delete.clicked.connect(self.delete_device)

        self.pushButton_add.clicked.connect(self.show_addWindow)

        # 选中行后使能删除按钮
        self.tableWidget_device.cellPressed.connect(self.getPosContent)

        self.Signal_Refresh_Data.connect(self.show_devices)

        self.pushButton_close.clicked.connect(self.close)

        self.pushButton_get_device.clicked.connect(self.get_device_ID)

        self.search_port()

        # 界面初始化
        self.InitInterface()

        self.show_devices()

    def resource_path(self, relative_path):
        """获取资源文件的绝对路径"""
        try:
            # PyInstaller创建临时文件夹，将路径存储在_MEIPASS中
            base_path = sys._MEIPASS
        except Exception:
            base_path = os.path.abspath(".")

        return os.path.join(base_path, relative_path)

    def showWindow(self):
        self.show()

    # 展示添加设备界面
    def show_addWindow(self):
        try:
            self.addWindow = AddWindow(self)
            self.addWindow.showWindow()
        except Exception as e:
            logger.error(f'呼出添加设备窗口错误：{e}')

    # 界面初始化
    def InitInterface(self):
        self.setWindowFlags(Qt.FramelessWindowHint)

        self.show_device_combBox()

        self.read_connection_ini()

    # 搜索串口
    def search_port(self):
        self.comboBox_Port.clear()

        availablePort = QSerialPortInfo.availablePorts()
        new_port = []
        for port in availablePort:
            new_port.append(port.portName())
        for port in new_port:
            self.comboBox_Port.addItem(port)

        self.comboBox_Port.setCurrentIndex(0)

    # 查询传感器并显示
    def show_device_combBox(self):
        try:
            conn = sqlite3.connect(self.resource_path('db/WTC.db'))
            cursor = conn.cursor()

            select_device = """ SELECT sensor_id FROM device_db"""
            cursor.execute(select_device)
            result = cursor.fetchall()
            for row in result:
                self.comboBox_sensor.addItem(str(row[0]))

        except Exception as e:
            logger.error(f'查询传感器错误：{e}')

    # 写配置文件存储通信配置
    def white_connection_ini(self, port, timeout, address, ambient_sensor):
        try:
            config = ConfigParser()
            config["AmbientSensor"] = {"ambient_sensor": ambient_sensor}

            config["Connection"] = {"port": port, "timeout": timeout, "address": address}

            # 保存配置
            with open("config.ini", "w") as f:
                config.write(f)
                self.read_connection_ini()
            QMessageBox.information(self, "成功", "修改配置成功")

        except Exception as e:
            print(e)

    # 读取配置文档并显示在界面
    def read_connection_ini(self):
        try:
            config = ConfigParser()
            config.read("config.ini")

            port = config.get("Connection", "port")
            timeout = config.get("Connection", "timeout")
            address = config.get("Connection", "address")
            ambient_sensor = config.get("AmbientSensor", "ambient_sensor")

            self.comboBox_Port.setCurrentText(port)
            self.comboBox_Freq.setCurrentText(timeout)
            self.lineedit_Address.setText(address)
            self.comboBox_sensor.setCurrentText(ambient_sensor)

        except Exception as e:
            logger.error(f'读取配置文件错误：{e}')
            print(e)

    def get_selected_rows(self):
        # 获取所有选中的行
        selected_rows = set()
        for item in self.tableWidget_device.selectedItems():
            selected_rows.add(item.row())

        rows_data = []
        for row in sorted(selected_rows):
            row_data = []
            for col in range(self.tableWidget_device.columnCount()):
                item = self.tableWidget_device.item(row, col)
                row_data.append(item.text() if item else "")
            rows_data.append(row_data)

        return sorted(rows_data)

    # 获取选中行列、内容
    def getPosContent(self, row, col):
        self.pushButton_delete.setEnabled(True)

    def config_sittings(self):
        try:
            if self.lineedit_Address == "" or not self.lineedit_Address.text().isdigit():
                QMessageBox.warning(self, "警告", "请填入正确的地址值")
                return
            else:
                port = self.comboBox_Port.currentText()
                timeout = self.comboBox_Freq.currentText()
                address = self.lineedit_Address.text()
                ambient_sensor = self.comboBox_sensor.currentText()

                self.white_connection_ini(port, timeout, address, ambient_sensor)
        except Exception as e:
            print(e)

    # 发送报文，获取测温主机上传感器ID
    def get_device_ID(self):
        # TODO: 用主线程的发送队列来发送一个询问传感器ID的报文，并显示在一个像窗口中

        # 小窗口表格弹出ID及位置，询问是否添加（全部替换，并同步到数据库中
        self.showDevice = ShowDevice(self)

    # 分析传感器列表报文
    def parse_sensorID(self, response):
        clean_hex = re.sub(r'\s', '', response.upper())

        if len(clean_hex) < 8 or len(clean_hex) % 2 != 0:
            raise ValueError(f"无效报文长度: {len(clean_hex)}字符")

        if not all(c in '0123456789ABCDEF' for c in clean_hex):
            raise ValueError("包含非法字符")

        function_code = int(clean_hex[2:4], 16)

        if function_code != 0x03:
            raise ValueError(f"不支持的功能码: 0x{function_code:02X}")

        data_bytes_length = int(clean_hex[4:6], 16)

        expected_length = 6 + data_bytes_length * 2 + 4
        if len(clean_hex) < expected_length:
            raise ValueError(f"报文过短: 需要{expected_length}字符，实际{len(clean_hex)}")

        frame_bytes = bytes.fromhex(clean_hex)

        crc_calculated = self.modbus_crc(frame_bytes[:-2])

        registers = []
        data_start = 6
        data_end = data_start + data_bytes_length * 2

        # 遍历数据部分（每4个十六进制字符为一个寄存器值）
        for i in range(data_start, data_end, 4):
            reg_hex = clean_hex[i:i + 4]
            reg_value = int(reg_hex, 16)

            # 遇到零值寄存器时停止输出
            if reg_value == 0:
                break

            registers.append(reg_value)

        return {
            "values": registers,
            "crc_valid": (crc_calculated == int.from_bytes(frame_bytes[-2:], 'little'))
        }

    def modbus_crc(self, data_bytes):
        crc = 0xFFFF
        for byte in data_bytes:
            crc ^= byte
            for _ in range(8):
                lsb = crc & 0x0001
                crc >>= 1
                if lsb:
                    crc ^= 0xA001
        return crc

    # 读取数据库设备数据并显示
    def show_devices(self):
        try:
            conn = sqlite3.connect(self.resource_path('db/WTC.db'))
            cursor = conn.cursor()

            read_table_devices = """
                         SELECT site, location, device, sensor_id FROM device_db;
                        """

            cursor.execute(read_table_devices)

            device_data = cursor.fetchall()
            # print(device_data)

        except Exception as e:
            logger.critical(f'打开数据库错误：{e}')
            print('打开数据库错误:', e)

        finally:
            if conn:
                conn.close()

        try:
            self.tableWidget_device.setRowCount(len(device_data))

            for row_idx, row_data in enumerate(device_data):
                for col_idx, cell_data in enumerate(row_data):
                    item = QTableWidgetItem(str(cell_data))
                    item.setTextAlignment(Qt.AlignCenter)
                    self.tableWidget_device.setItem(row_idx, col_idx, item)

        except Exception as e:
            print('读取数据库错误：', e)
            logger.critical(f'读取数据库错误：{e}')

    # 删除数据库设备数据
    def delete_device(self, sensor_id):
        try:
            selected_rows = self.get_selected_rows()

            conn = sqlite3.connect(self.resource_path('db/WTC.db'))
            cursor = conn.cursor()

            for row in selected_rows:
                delete_data_devices = 'DELETE FROM device_db WHERE sensor_id = ?'
                print(row[3])
                cursor.execute(delete_data_devices, (row[3],))
            cursor.fetchall()
            conn.commit()

        except Exception as e:
            logger.critical(f'删除设备表数据错误：{e}')
        finally:
            self.show_devices()

            if conn:
                conn.close()

    def is_row_empty(self, row):
        empty = True
        for col in range(1, self.tableWidget_device.columnCount()):
            item = self.tableWidget_device.item(row, col)
            item.setTextAlignment(Qt.AlignCenter)
            if item and item.text().strip():
                empty = False
                break
        return empty

    def mouseMoveEvent(self, e: QMouseEvent):  # 重写移动事件
        if self._tracking:
            self._endPos = e.pos() - self._startPos
            self.move(self.pos() + self._endPos)

    def mousePressEvent(self, e: QMouseEvent):
        if e.button() == Qt.LeftButton:
            self._startPos = QPoint(e.x(), e.y())
            self._tracking = True

    def mouseReleaseEvent(self, e: QMouseEvent):
        if e.button() == Qt.LeftButton:
            self._tracking = False
            self._startPos = None
            self._endPos = None

    def closeEvent(self, event):
        self.finished.emit()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    mainWindow = SettingWindow()
    mainWindow.show()
    sys.exit(app.exec_())
